% !TeX program = xelatex 
\documentclass[xcolor=svgnames,10pt,UTF8]{ctexbeamer}
%\documentclass[aspectratio=169,xcolor=svgnames,10pt]{beamer}

\RequirePackage{slide}

%\logo{\includegraphics[scale=0.015]{../figure/logo.jpg}}
\title{SystemVerilog芯片验证}
\subtitle{第7章~进程间通信}
%\author{王旭}
%\institute{深圳信息职业技术学院~微电子学院}
\date{\today}

\begin{document}

\frame{\titlepage}
%\LogoOn
%\LogoOff

\section{块语句和进程} \label{Parallel Blocks and processs}

\subsection{块语句} \label{subsec Parallel Blocks}

\begin{frame}[t,fragile]{块语句}

	块语句（block statement）可以将一些语句组合在一起，使它们在语法上就像一条语句一样，块语句包含如下两种类型。

	\begin{enumerate}
		\item 顺序块（sequential block），也被称为begin-end块，它由关键字begin和end分隔。顺序块不启动新的子进程，块中的过程语句按前后顺序逐条被执行。
		\item 并行块（parallel block），也被称为fork-join块，它由关键字fork和join、join\_any或join\_none分隔。并行块可以包含顺序块，并行块中的顺序块和过程语句都会各自启动一个新的子进程，这些子进程同时被执行。
	\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{并行块}

\begin{enumerate}
	\item fork-join块：阻塞主进程，启动所有子进程，当最慢的子进程执行完成后，主进程才恢复运行。
	\item fork-join\_any块：阻塞主进程，启动所有子进程，当最快的子进程执行完成后，主进程才与剩余的子进程同时运行。
	\item fork-join\_none块：不阻塞主进程，主进程和所有子进程同时运行，这是最常用的一种并行块。
\end{enumerate}

\begin{figure}[!htb]
	\centering
	\includegraphics[scale=0.8]{fork_statements.pdf}
	%\caption{3种并行块的运行方式} \label{fork statements}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{}

并行块中的顺序块和过程语句都会各自启动一个新的子进程。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-21},consecutivenumbers=false]
{src/ch7/sec1/1/test.sv}

\end{frame}

\begin{frame}[t,fragile]{fork-join}

\begin{lstlisting}
@0: Line 6, proc
@10000: Line 7, proc
@10000: Line 9, subproc 1
@20000: Line 15, subproc 4
@30000: Line 12, subproc 3-1
@40000: Line 10, subproc 2
@50000: Line 13, subproc 3-2
@50000: Line 17, proc
@60000: Line 18, proc
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{fork-join\_none}

\begin{lstlisting}
@0: Line 6, proc
@10000: Line 7, proc
@10000: Line 17, proc
@10000: Line 9, subproc 1
@20000: Line 18, proc
@20000: Line 15, subproc 4
@30000: Line 12, subproc 3-1
@40000: Line 10, subproc 2
@50000: Line 13, subproc 3-2
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{fork-join\_any}

\begin{lstlisting}
@0: Line 6, proc
@10000: Line 7, proc
@10000: Line 9, subproc 1
@10000: Line 17, proc
@20000: Line 18, proc
@20000: Line 15, subproc 4
@30000: Line 12, subproc 3-1
@40000: Line 10, subproc 2
@50000: Line 13, subproc 3-2
\end{lstlisting}

\end{frame}

\subsection{动态子进程} \label{Dynamic subprocesss}

\begin{frame}[t,fragile]{动态子进程}

为便于说明，首先定义一个带有copy方法的transaction类。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
{src/ch7/sec1/2/transaction.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-33},consecutivenumbers=false]
{src/ch7/sec1/2/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\begin{lstlisting}
generate: a=9, b=12
generate: a=11, b=0
generate: a=10, b=6
generate: a=7, b=2
generate: a=0, b=12
generate: a=3, b=6
@0,transmit: a=0, b=12
@30000,transmit: a=3, b=6
@70000,transmit: a=7, b=2
@90000,transmit: a=9, b=12
@100000,transmit: a=10, b=6
@110000,transmit: a=11, b=0
\end{lstlisting}

\end{frame}

\subsection{并行块中的自动变量} \label{Automatic Variables in Threads}

\begin{frame}[t,fragile]{避免在并行块中使用静态变量}

尽量避免在并行块中使用静态变量，因为主进程和并行块产生的所有子进程会共享静态变量。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-12},consecutivenumbers=false]
{src/ch7/sec1/3/test.sv}

\begin{lstlisting}
i=3
i=3
i=3
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{在并行块中定义局部自动变量}

正确的方法是在并行块中定义局部自动变量k，用来保存静态变量i的值。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-13},consecutivenumbers=false]
{src/ch7/sec1/4/test.sv}

\begin{lstlisting}
i=0
i=1
i=2
\end{lstlisting}

\end{frame}

\section{进程控制} \label{Processs Control}

\subsection{wait fork} \label{Waiting for all Spawned processs}

\begin{frame}[t,fragile]{wait fork}

wait fork语句会阻塞主进程的执行，直到主进程所启动的所有子进程执行完成。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
{src/ch7/sec2/1/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

使用wait fork运行结果。

\begin{lstlisting}
@0: Line 13, proc
@0: Line 7, subproc 1
@20000: Line 8, subproc 2
@30000: Line 16, proc
\end{lstlisting}

去掉wait fork运行结果。这时主进程与所有子进程并行执行，主进程中延迟较短的第16行语句会先被执行，延迟较长的第8行的第二个子进程后被执行。

\begin{lstlisting}
@0: Line 13, proc
@0: Line 7, subproc 1
@10000: Line 16, proc
@20000: Line 8, subproc 2
\end{lstlisting}

\end{frame}

\subsection{disable} \label{Disabling a Single process}

\begin{frame}[t,fragile]{disable}

disable语句可以停止一个带有标识符的并行块中的所有活动子进程。使用disable语句可能会不经意地停止并行块中过多的子进程。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-20},consecutivenumbers=false]
{src/ch7/sec2/2/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={22-35},consecutivenumbers=false]
{src/ch7/sec2/2/test.sv}

\begin{lstlisting}
generate: a=9, b=12, sum=0
generate: a=11, b=0, sum=0
generate: a=10, b=6, sum=0
generate: a=7, b=2, sum=0
generate: a=0, b=12, sum=0
generate: a=3, b=6, sum=0
generate: a=10, b=12, sum=0
generate: a=3, b=14, sum=0
@0: check 0
\end{lstlisting}

\end{frame}

\subsection{disable fork} \label{Disabling Multiple processs}

\begin{frame}[t,fragile]{disable fork}

disable fork语句只结束并行块中当前活动的子进程，并行块中的其他子进程不受影响。

\begin{lstlisting}
generate: a=9, b=12, sum=0
generate: a=11, b=0, sum=0
generate: a=10, b=6, sum=0
generate: a=7, b=2, sum=0
generate: a=0, b=12, sum=0
generate: a=3, b=6, sum=0
generate: a=10, b=12, sum=0
generate: a=3, b=14, sum=0
@0: check 0
@30000: check 3
@30000: check 3
@40000: check a=9 timeout.
@40000: check a=11 timeout.
@40000: check a=10 timeout.
@40000: check a=7 timeout.
@40000: check a=10 timeout.
\end{lstlisting}

\end{frame}

\subsection{进程类} \label{subsec Fine-grain Processs Control}

\begin{frame}[t,fragile]{进程类process}

进程类process是SystemVerilog的一个内置类。进程类不能派生，也不能调用构造函数手动创建进程对象。每个进程启动后会在其内部自动创建一个进程对象，进程对象就代表它所在的进程，调用进程类的方法，就可以控制进程对象所在的进程。

\begin{lstlisting}[language=SystemVerilog,numbers=left,consecutivenumbers=false]
class process;
	typedef enum {FINISHED, RUNNING, WAITING, SUSPENDED, KILLED} state;
	
	static function process self();
	function state status();
	function void kill();
	task await();
	function void suspend();
	function void resume();
	function void srandom(int seed);
	function string get_randstate();
	function void set_randstate(string state);
endclass
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{进程类中的部分方法说明}

\begin{enumerate}
	\item self方法：返回进程对象的句柄，即获得进程对象所在的进程的访问和控制权。
	\item status方法：返回进程对象所在的进程的状态。返回值FINISHED表示进程已经正常结束，返回值RUNNING表示进程正在运行，返回值WAITING表示进程正处于阻塞状态，返回值SUSPENDED表示进程被暂停，返回值KILLED表示进程被强制结束。
	\item kill方法：停止进程对象所在的进程，包括该进程中的所有子进程。
	\item await方法：在一个进程中等待另一个进程运行结束，注意不能在当前进程中调用自身的await方法。
	\item suspend方法：允许一个进程暂停自己或其他进程的执行。
	\item resume方法：重启之前被暂停的进程。
\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-27},consecutivenumbers=false]
{src/ch7/sec2/3/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={28-32},consecutivenumbers=false]
{src/ch7/sec2/3/test.sv}

\begin{lstlisting}
start subprocess0
start subprocess1
start subprocess2
start subprocess3
subprocess0.status=FINISHED
subprocess1.status=FINISHED
subprocess2.status=FINISHED
subprocess3.status=WAITING
subprocess3.status=KILLED
\end{lstlisting}

\end{frame}

\section{事件的触发和等待} \label{Events}

\subsection{阻塞事件与非阻塞事件} \label{Blocking on the Edge of an Event}

\begin{frame}[t,fragile]{操作符“->”用来触发阻塞事件}

因为e1和e2都是阻塞事件，所以e1触发后e2才能触发，这就导致第二个initial过程错过事件e1而被阻塞。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-22},consecutivenumbers=false]
{src/ch7/sec3/1/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={24-29},consecutivenumbers=false]
{src/ch7/sec3/1/test.sv}

\begin{lstlisting}
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger
proc1.status=FINISHED
proc2.status=WAITING
\end{lstlisting}

\end{frame}

\begin{frame}[t,fragile]{操作符“->>”用来触发非阻塞事件}

操作符“\verb|->>|”用来触发非阻塞事件，非阻塞事件是并行发生的，没有先后顺序区分。

\begin{lstlisting}
@0: 1: before trigger
@0: 2: before trigger
@0: 2: after trigger
@0: 1: after trigger
proc1.status=FINISHED
proc2.status=FINISHED
\end{lstlisting}

\end{frame}

\subsection{triggered方法} \label{Waiting for an Event Trigger}

\begin{frame}[t,fragile]{triggered方法}

事件类的内置方法triggered，它用于检查一个事件在当前时间片内是否已被触发。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-29},consecutivenumbers=false]
{src/ch7/sec3/2/test.sv}

\end{frame}

\subsection{传递事件} \label{Passing Events}

\begin{frame}[t,fragile]{事件是事件句柄的简称，传递事件等价于传递事件句柄}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-19},consecutivenumbers=false]
{src/ch7/sec3/3/sequencer.svh}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-14},consecutivenumbers=false]
{src/ch7/sec3/3/test.sv}

\end{frame}

\subsection{等待多个事件} \label{Waiting for Multiple Events}

\begin{frame}[t,fragile]{等待多个事件的几种方法}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-27},consecutivenumbers=false]
{src/ch7/sec3/4/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={6-29},consecutivenumbers=false]
{src/ch7/sec3/5/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-13},consecutivenumbers=false]
{src/ch7/sec3/6/sequencer.svh}

测试模块中使用wait语句等待sequencer类中的静态属性proc\_cnt恢复到0。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={8-17},consecutivenumbers=false]
{src/ch7/sec3/6/test.sv}

\end{frame}

\section{信号量} \label{Semaphores}

\begin{frame}[t,fragile]{信号量（semaphore）是一个内置类}

信号量通常被用来控制对共享资源的互斥访问，以及用来实现基本的同步。假设在计算机系统中，有两个进程试图访问同一个共享内存区域，其中一个进程试图写入数据，而另一个进程试图读取相同内存地址的数据。两个进程同时执行将导致一个不可预测的结果，信号量可以用来解决这种互斥操作。

\begin{enumerate}
	\item 信号量对象中钥匙的数量可以比进程需要的多。例如信号量对象有两个钥匙，而进程只需要一个钥匙。
	\item 如果在当前时间信号量对象只有一个钥匙，而FIFO中最前面的进程需要获取多个钥匙，此时这个进程会被阻塞。如果排在后面的进程只需要一个钥匙，那么它会绕过FIFO顺序排到进程的最前面，从信号量对象中获取到钥匙。
\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{信号量类的常见方法包括如下}

\begin{enumerate}
	\item new方法：创建带有一个或多个钥匙的信号量对象。
	\item get方法：获取一个或多个钥匙。
	\item put方法：返回一个或多个钥匙。
	\item try\_get方法：尝试获取1个钥匙但是不会执行阻塞，如果钥匙足够多，try\_get会获取它们并返回1，如果没有足够的钥匙它只返回0。
\end{enumerate}

\end{frame}

\begin{frame}[t,fragile]{信号量使用的例子}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-19},consecutivenumbers=false]
{src/ch7/sec4/1/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={20-32},consecutivenumbers=false]
{src/ch7/sec4/1/test.sv}

\begin{lstlisting}
Run proc1 at 10000.
Run proc2 at 10000.
Run proc3 at 20000.
\end{lstlisting}

\end{frame}

\section{信箱} \label{Mailboxes}

\begin{frame}[t,fragile]{信箱}

信箱（mailbox）是一种在进程之间交换数据的机制，信箱类似一个FIFO，可以发送任意数据类型的数据。向装满的信箱中放入数据，或从空信箱中取出数据时，进程都会被阻塞。

\begin{figure}[!htb]
	\centering\captionsetup{font={footnotesize}}\footnotesize
	\scalebox{0.8}{
		\begin{tikzpicture}
			\tikzstyle{every node}=[font=\normalsize]
			\node[thick,draw,minimum height=1.0cm,minimum width=3.0cm] (sequencer) at (0,0) {sequencer};
			\node[thick,draw,minimum height=0.5cm,minimum width=2.0cm] at (2.5,0) {\ding{41}\ \ding{41}\ \ding{41}\ \ding{41}\ \ding{41}};
			\node[thick,draw,minimum height=1.0cm,minimum width=2.0cm] (driver) at (4.5,0) {driver};
			\node at (2.5,-0.5) {信箱};
		\end{tikzpicture}
	}
	%\caption{连接sequencer类和driver类的信箱} \label{A mailbox connecting two transactors}
\end{figure}

\begin{table}[!htb]
	\centering\captionsetup{font={footnotesize}}\footnotesize %small or scriptsize
	%\caption{信箱的内置方法} \label{build in methods of mailbox}%
	\begin{tabular}{ll}
		\hline
		\rowcolor{black!10}方法名                         & 功能                              \\\hline
		\lstinline[language=SystemVerilog]|new(n)|     & 创建一个信箱                          \\\hline
		\lstinline[language=SystemVerilog]|put()|      & 将一个数据放进信箱，如果信箱已满则阻塞             \\\hline
		\lstinline[language=SystemVerilog]|try_put()|  & 尝试将一个数据放进信箱，如果信箱已满则放弃操作         \\\hline
		\lstinline[language=SystemVerilog]|get()|      & 从信箱中取出并删除一个数据，如果信箱为空则阻塞         \\\hline
		\lstinline[language=SystemVerilog]|try_get()|  & 从信箱中取出并删除一个数据，如果信箱为空则放弃操作       \\\hline
		\lstinline[language=SystemVerilog]|peek()|     & 从信箱中复制一个数据，但不删除这个数据，如果信箱为空则阻塞   \\\hline
		\lstinline[language=SystemVerilog]|try_peek()| & 从信箱中复制一个数据，但不删除这个数据，如果信箱为空则放弃操作 \\\hline
		\lstinline[language=SystemVerilog]|num()|      & 返回信箱中数据的个数                      \\\hline
	\end{tabular}%
\end{table}

默认情况下信箱没有类型，信箱可以放入各种数据，但为了方便处理数据，规定信箱只存放一种类型的数据。

\end{frame}

\subsection{使用信箱发送事务} \label{Using mailbox to put transaction}

\begin{frame}[t,fragile]{使用信箱发送事务}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-23},consecutivenumbers=false]
{src/ch7/sec5/1/sequencer.svh}

\end{frame}

\subsection{使用信箱接收事务} \label{Using mailbox to get transaction}

\begin{frame}[t,fragile]{使用信箱接收事务}

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-18},consecutivenumbers=false]
	{src/ch7/sec5/1/driver.svh}

\begin{figure}[!htb]
	\centering\captionsetup{font={footnotesize}}\footnotesize
	\scalebox{0.8}{
		\begin{tikzpicture}
			\tikzstyle{every node}=[font=\normalsize]
			%\draw[thick,color=gray] (0,0) grid (12,4);
			\foreach \x/\obj in {1/86,3/85,4/84,5/83,6/82,8/81}
				{	\draw (\x,3) node (mail\x) {\ding{41}};
					\node (copy\x) at (\x,1.5) {\ding{\obj}};
					\draw[thick,-Latex] (mail\x) -- (copy\x);}
			\draw[thick] (2,2.5) rectangle (7,3.5);
			\draw[thick,dashed,-Latex] (mail1) -- (mail3);
			\draw[thick,dashed,-Latex] (mail6) -- (mail8);
		\end{tikzpicture}
	}
	%\caption{信箱中的句柄指向各自的随机化对象副本} \label{A mailbox with multiple handles to multiple objects}
\end{figure}

\end{frame}

\begin{frame}[t,fragile]{}

	\begin{enumerate}
		\item 如果不希望在访问信箱时出现阻塞，可以使用try\_get和try\_peek方法。如果执行成功，它们会返回一个非零值，否则返回0。
		\item 使用new方法构造信箱时默认容量是0，表示创建一个容量无限的信箱。任何大于0的容量都会创建一个有限信箱。如果试图放入的对象数量大于最大容量，则put方法会处于阻塞状态直到有对象从信箱中取出。
	\end{enumerate}

\end{frame}

\subsection{使用信箱交换事务} \label{Mailbox in a Testbench}

\begin{frame}[t,fragile]{使用信箱交换事务}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={8-26},consecutivenumbers=false]
{src/ch7/sec5/1/test.sv}

\end{frame}

\section{进程同步} \label{Processs ynchronization}

\subsection{使用事件同步进程} \label{Unsynchronized processs Communicating with a Mailbox}

\begin{frame}[t,fragile]{使用事件同步进程}

握手信号可以使用事件实现。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-26},consecutivenumbers=false]
{src/ch7/sec6/1/sequencer.svh}

\end{frame}

\begin{frame}[t,fragile]{}

驱动器在完成事务处理后触发事件handshake，解除序列发生器的阻塞，从而实现序列发生器和驱动器的进程同步。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-21},consecutivenumbers=false]
{src/ch7/sec6/1/driver.svh}

\end{frame}

\begin{frame}[t,fragile]{}

测试模块中只需要定义事件handshake，并将它分别传入序列发生器和驱动器。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={8-27},consecutivenumbers=false]
{src/ch7/sec6/1/test.sv}

\end{frame}

\begin{frame}[t,fragile]{}

	序列发生器发送完一个事务后，会等待驱动器接收完成后再继续发送新事务。

\begin{lstlisting}
sequencer: a=9, b=12
driver: a=9, b=12
sequencer: a=11, b=0
driver: a=11, b=0
sequencer: a=10, b=6
driver: a=10, b=6
sequencer: a=7, b=2
driver: a=7, b=2
\end{lstlisting}

\end{frame}

\subsection{使用信箱同步进程} \label{Synchronized processs Using Mailboxes}

\begin{frame}[t,fragile]{握手信号也可以使用信箱实现}

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-27},consecutivenumbers=false]
{src/ch7/sec6/2/sequencer.svh}

\end{frame}

\begin{frame}[t,fragile]{}

driver使用信箱seqr2drv的get方法接收到事务对象后，会使用信箱handshake的put方法向sequencer反馈一个byte类型的握手信号。

\lstinputlisting[language=SystemVerilog,numbers=left,linerange={4-21},consecutivenumbers=false]
{src/ch7/sec6/2/driver.svh}

\end{frame}

\begin{frame}[t,fragile]{}

	\lstinputlisting[language=SystemVerilog,numbers=left,linerange={8-27},consecutivenumbers=false]
{src/ch7/sec6/2/test.sv}

\end{frame}

\end{document}
